# -*- coding: utf-8 -*-
import argparse
import configparser
import sys

import pymysql.cursors
from keystoneauth1 import identity
from keystoneauth1 import session
from neutronclient.v2_0 import client

from f5.bigip import ManagementRoot
from f5_openstack_agent.lbaasv2.drivers.bigip.cluster_manager \
    import ClusterManager
from f5_openstack_agent.lbaasv2.drivers.bigip.resource_helper \
    import ResourceType
from f5_openstack_agent.lbaasv2.drivers.bigip.resource_helper \
    import BigIPResourceHelper


ACTIVE_DEVICE = 'ACTIVE_DEVICE'
STANDBY_DEVICE = 'STANDBY_DEVICE'


class SelfipCreate:
    def __init__(self, conf=None):
        self.conf = conf
        self.cluster_manager = ClusterManager()
        self.active = self._open_bigip(self.conf.get('default', 'active_hostname'),
                                       self.conf.get('default', 'active_username'),
                                       self.conf.get('default', 'active_password'))
        self.active_name = self._get_device_name(self.active)
        self.standby = self._open_bigip(self.conf.get('default', 'standby_hostname'),
                                        self.conf.get('default', 'standby_username'),
                                        self.conf.get('default', 'standby_password'))
        self.standby_name = self._get_device_name(self.standby)
        # self.conn = self._connect_db()
        self.neutron = self._get_neutron_client()
        self.selfip_names = self._get_selfip_name_list()
        self.selfip_manager = BigIPResourceHelper(ResourceType.selfip)
        # 创建的port对应哪台BIG-IP
        self.create_device = None

    def _get_device_name(self, bigip):
        name = self.cluster_manager.get_device_name(bigip),
        if type(name) is tuple or list:
            return name[0]
        return name

    def _open_bigip(self, hostname, username, password):
        try:
            bigip = ManagementRoot(hostname=hostname,
                                   username=username,
                                   password=password,
                                   port='443',
                                   token=True,
                                   timeout=30)
            return bigip
        except Exception as e:
            print('Fail to connect BIG-IP: {}, error: {}'.format(hostname, e))
            raise e

    def _get_neutron_client(self):
        auth = identity.Password(auth_url=self.conf.get('default', 'os_auth_url'),
                                 username=self.conf.get('default', 'os_username'),
                                 password=self.conf.get('default', 'os_password'),
                                 project_name=self.conf.get('default', 'os_project_name'),
                                 project_domain_id=self.conf.get('default', 'os_project_domain_id'),
                                 user_domain_id=self.conf.get('default', 'os_user_domain_id'))
        sess = session.Session(auth=auth)

        region_name = self.conf.get('default', 'region_name')
        neutron = client.Client(session=sess, region_name=region_name)
        return neutron

    def _connect_db(self):
        db_conn = self.conf.get('default', 'connection')
        db_type = db_conn[0:db_conn.find("+")]
        if db_type != "mysql":
            print("Only support MySQL DB")
            sys.exit(1)

        u_start = db_conn.find("//") + 2
        u_end = db_conn.find(":", u_start)
        p_start = u_end + 1
        p_end = db_conn.find("@", p_start)
        h_start = p_end + 1
        h_end = db_conn.find("/", h_start)
        d_start = h_end + 1

        return pymysql.connect(host=db_conn[h_start:h_end],
                               user=db_conn[u_start:u_end],
                               password=db_conn[p_start:p_end],
                               database=db_conn[d_start:],
                               cursorclass=pymysql.cursors.DictCursor)

    def _get_selfip_name_list(self):
        return self.conf.get('default', 'selfip_names').split(',')

    def run(self):
        # 顺序创建缺失的selfip
        for selfip_name in self.selfip_names:
            self.create(selfip_name)

    def create(self, selfip_name):
        # 调用client查询selfip_name是否存在；不存在则创建port
        # 查询selfip是否在BIG-IP存在；不存在则在BIG-IP创建
        self.create_port(selfip_name)

    def create_port(self, selfip_name):
        # 创建selfip port
        # 查询port是否存在，存在则返回，继续在BIG-IP执行
        # 不存在计算出另一台selfip的name，查询port是否存在。若也不存在，则写入输出文件。两个port都缺失，需锐捷手工处理。
        # 另一台存在，则查询其port信息，copy信息并修改成需要创建的selfip。调用api创建port。
        # 然后把这两个selfip都去BIG-IP创建一次。

        selfip_ports = self.neutron.list_ports(name=selfip_name)
        # print('get ports by rma_self_name: {}, selfip_ports: {}'.format(selfip_name, selfip_ports))
        if len(selfip_ports['ports']) != 0:
            print('selfip: {} exists in neutron db'.format(selfip_name))
            return

        pair_selfip_name = self._get_pair_selfip_name(selfip_name)
        pair_ports = self.neutron.list_ports(name=pair_selfip_name)
        if len(pair_ports['ports']) == 0:
            msg = 'pair port: {} not exists in neutron db, please check'.format(pair_selfip_name)
            print(msg)
            raise Exception(msg)

        pair_port = pair_ports['ports'][0]
        subnet_id = pair_port['fixed_ips'][0]['subnet_id']
        create_port_body = {
            'admin_state_up': True,
            'binding:host_id': pair_port['binding:host_id'],
            'binding:profile': pair_port['binding:profile'],
            'binding:vnic_type': pair_port['binding:vnic_type'],
            'device_id': pair_port['device_id'],
            'device_owner': pair_port['device_owner'],
            'fixed_ips': [{'subnet_id': subnet_id}],
            'name': selfip_name,
            'network_id': pair_port['network_id'],
            'port_security_enabled': pair_port['port_security_enabled'],
            'project_id': pair_port['project_id'],
            'tenant_id': pair_port['tenant_id'],
        }

        self.neutron.create_port({'port': create_port_body})
        new_create_ports = self.neutron.list_ports(name=selfip_name)
        # new_port_id = new_create_ports['ports'][0]['id']
        new_ip_address = new_create_ports['ports'][0]['fixed_ips'][0]['ip_address']
        print('create new selfip port: {}, ip address is: {}'.format(selfip_name, new_ip_address))

        # 使用直连数据库的方式修改port status为ACTIVE
        # self.set_port_status_active(new_port_id)
        # print('update port: {} of selfip: {}, set status ACTIVE'.format(new_port_id, selfip_name))

        print('Create selfip: {} successful'.format(selfip_name))

    def set_port_status_active(self, rma_port_id):
        with self.conn:
            with self.conn.cursor() as cursor:
                sql = """
                UPDATE ports SET status=%s WHERE id=%s
                """
                cursor.execute(sql, ("ACTIVE", rma_port_id))

    def _get_pair_selfip_name(self, selfip_name):
        pair_name = None
        if self.active_name in selfip_name:
            self.create_device = ACTIVE_DEVICE
            pair_name = selfip_name.replace(self.active_name, self.standby_name)
        if self.standby_name in selfip_name:
            self.create_device = STANDBY_DEVICE
            pair_name = selfip_name.replace(self.standby_name, self.active_name)
        if not pair_name:
            raise Exception('get pair_selfip_name failed by selfip_name: {}'.format(selfip_name))
        return pair_name

    def create_bigip_config(self, selfip_name):
        # 在BIG-IP创建self ip
        # 先查询是否存在，存在则返回。
        # 不存在则创建。
        pass

    def prepare_selfip_body(self):
        pass

    def _create_bigip_selfip(self, bigip, model):
        if self.selfip_manager.exists(bigip, name=model['name'],
                                      partition=model['partition']):
            print('selfip config exists in BIG-IP')
        else:
            try:
                self.selfip_manager.create(bigip, model)
            except Exception as err:
                msg = 'create selfip in BIG-IP failed, error: {}'.format(err)
                raise Exception(msg)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(usage="python selfip_create.py "
                                           "--config-path='/selfip/create/path/selfip_create_conf.ini'")
    parser.add_argument('--config-path', type=str, help='selfip_create_conf.ini absolute path', required=True)
    args = parser.parse_args()

    config = configparser.ConfigParser()
    print("read from config file: {}".format(args.config_path))
    config.read(args.config_path, encoding='UTF-8')

    selfip_creator = SelfipCreate(config)
    selfip_creator.run()
